/*
 *  Fetch & Decode module for Zet
 *  Copyright (C) 2010  Zeus Gomez Marmolejo <zeus@aluzina.org>
 *
 *  This file is part of the Zet processor. This processor is free
 *  hardware; you can redistribute it and/or modify it under the terms of
 *  the GNU General Public License as published by the Free Software
 *  Foundation; either version 3, or (at your option) any later version.
 *
 *  Zet is distrubuted in the hope that it will be useful, but WITHOUT
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
 *  License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Zet; see the file COPYING. If not, see
 *  <http://www.gnu.org/licenses/>.
 */

`timescale 1ns/10ps

`include "defines.v"

module zet_fetch (
    input clk,
    input rst,

    // to decode
    output     [7:0] opcode,
    output     [7:0] modrm,
    output           rep,
    //output           exec_st,
    output           ld_base,
    output reg [2:0] sop_l,

    // from decode
    input need_modrm,
    input need_off,
    input need_imm,
    input off_size,
    input imm_size,
    input ext_int,
    input end_seq,

    // to microcode
    //output reg [15:0] off_l,
    //output reg [15:0] imm_l,

    // from microcode
    input [5:0] ftype,

    // to exec
    //output [15:0] imm_f,

    input [15:0] cs,
    input [15:0] ip,
    input of,
    input zf,
    input cx_zero,
    input [15:0] data,
    output [19:0] pc,
    output bytefetch,
    input  block,
    input  div_exc,
    input  tflm,
    output wr_ip0,
    input  intr,
    input  iflm,
    input  nmir,
    input  iflss,

		// NEW
		output cache_line_not_full,
		input new_data,
		input [15:0] cpu_word_i,

		output [(`CACHE_LINE_SIZE*8)-1:0] cache_line_data,

		// from decode
		input [4:0] shift_cache_line,
		input [3:0] instr_length,
		input we_shift,

		//to decode
		output exec_st
  );

	// NEW
	wire [4:0] bytes_in_cache_line;
	//wire cache_not_full;
	reg [15:0] __old_ip;
	reg __restore_ip;

	// Registers, nets and parameters
	parameter opcod_st = 3'h0;
	parameter modrm_st = 3'h1;
	parameter offse_st = 3'h2;
	parameter immed_st = 3'h3;
	parameter execu_st = 3'h4;

	reg  [2:0] state;
	wire [2:0] next_state;

	wire prefix, repz_pr, sovr_pr, lock_pr;
	wire next_in_opco, next_in_exec;

	reg [7:0] opcode_l, modrm_l;
	reg [1:0] pref_l;
	reg       lock_l;

	// Module instantiation
	zet_next_or_not next_or_not(pref_l, opcode[7:1], cx_zero, zf, ext_int, next_in_opco, next_in_exec);

	zet_nstate nstate (state, prefix, need_modrm, need_off, need_imm, end_seq,
		 ftype, of, next_in_opco, next_in_exec, block, div_exc,
		 tflm, intr, iflm, nmir, iflss, next_state, cache_line_not_full);

	// NEW
	zet_cache_line cache_line(clk, rst, cpu_word_i, shift_cache_line, new_data, we_shift, cache_line_not_full, bytes_in_cache_line, cache_line_data);

	// Assignments

	// NEW
	
	assign pc = ((cs << 4) + ip + bytes_in_cache_line);

	assign opcode = (state == opcod_st) ? data[7:0] : opcode_l;
	assign modrm  = (state == modrm_st) ? data[7:0] : modrm_l;
	assign bytefetch = (state == offse_st) ? ~off_size
		       : ((state == immed_st) ? ~imm_size : 1'b1);
	assign exec_st = (state == execu_st);
	assign wr_ip0 = (state == opcod_st) && !pref_l[1] && !sop_l[2] && !lock_l;

	assign sovr_pr = (opcode[7:5]==3'b001 && opcode[2:0]==3'b110);
	assign repz_pr = (opcode[7:1]==7'b1111_001);
	assign lock_pr = (opcode[7:0]==8'b1111_0000);
	assign prefix  = sovr_pr || repz_pr || lock_pr;
	assign ld_base = (next_state == execu_st);
	assign rep     = pref_l[1];

	// Behaviour
	always @(posedge clk)
	if (rst)
		begin
			state <= execu_st;
			opcode_l <= `OP_NOP;
		end

	else if (!block)
		case (next_state)

			/* next state = opcode or prefix (0) */

			default:
			  begin

						case (state)
							opcod_st:
								begin // There has been a prefix
									pref_l <= repz_pr ? { 1'b1, opcode[0] }
												// clear prefixes on next instr
												: next_in_opco ? 2'b0 : pref_l;
									sop_l  <= sovr_pr ? { 1'b1, opcode[4:3] }
												// clear prefixes on next instr
												: next_in_opco ? 3'b0 : sop_l;
									lock_l <= lock_pr ? 1'b1
												// clear prefixes on next instr
												: next_in_opco ? 1'b0 : lock_l;
								end

							default: begin pref_l <= 2'b0; sop_l <= 3'b0; lock_l <= 1'b0; end

						endcase

						state <= opcod_st;
						//off_l <= 16'd0;
						modrm_l <= 8'b0000_0110;
					
			  end

			/* next_state = modrm (1) */

			modrm_st:
			  begin
					opcode_l  <= data[7:0];
					state <= modrm_st;
			  end

			/* next_state = offset (2) */

			offse_st:
			  begin

					case (state)
						opcod_st: opcode_l <= data[7:0];
						default: modrm_l <= data[7:0];
					endcase

					state <= offse_st;

			  end

			/* next_state = immediate (3) */

			immed_st:
			  begin

					case (state)
						opcod_st: opcode_l <= data[7:0];
						modrm_st: modrm_l <= data[7:0];
						//default: off_l <= data;
					endcase

					state <= immed_st;

			  end

			/* next_state = execute (4) */

			execu_st:
			  begin

					case (state)
						opcod_st: opcode_l <= data[7:0];
						modrm_st: modrm_l <= data[7:0];
						//offse_st: off_l <= data;
						//immed_st: imm_l <= data;
					endcase

					state <= execu_st;

			  end

		endcase

endmodule

